package com.ljk.util.date;


import java.time.*;
import java.time.format.DateTimeFormatter;
import java.time.format.TextStyle;
import java.time.temporal.*;
import java.util.*;

import static com.ljk.util.date.DateConstant.*;


/**
 * Java8日期时间工具类
 *
 * @author ljk
 * @since 2024/4/18 10:21
 */
public class LocalDateUtil {

    /**
     * 获取日期时间字符串
     *
     * @param temporal 需要转化的日期时间
     * @param pattern  时间格式
     * @return String 日期时间字符串，例如 2015-08-11 09:51:53
     */
    public static String format(TemporalAccessor temporal, String pattern) {
        if (temporal == null) {
            return null;
        }
        return DateTimeFormatter.ofPattern(pattern).format(temporal);
    }

    public static Date toDate(LocalDateTime dateTime) {
        if (dateTime == null) {
            return null;
        }
        return Date.from(dateTime.atZone(ZoneId.systemDefault()).toInstant());
    }

    public static Date toDate(LocalDate date) {
        if (date == null) {
            return null;
        }
        return Date.from(date.atStartOfDay(ZoneId.systemDefault()).toInstant());
    }

    public static Date toDate(LocalDate date, LocalTime time) {
        if (date == null || time == null) {
            return null;
        }
        return Date.from(time.atDate(date).atZone(ZoneId.systemDefault()).toInstant());
    }

    public static LocalDateTime parseLocalDateTime(Date date) {
        if (date == null) {
            return null;
        }
        return date.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();
    }

    public static LocalDate parseLocalDate(Date date) {
        if (date == null) {
            return null;
        }
        return date.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
    }

    public static LocalTime parseLocalTime(Date date) {
        if (date == null) {
            return null;
        }
        return date.toInstant().atZone(ZoneId.systemDefault()).toLocalTime();
    }

    /**
     * 日期时间字符串转换为日期时间(java.time.LocalDateTime)
     *
     * @param localDateTimeStr 日期时间字符串
     * @param pattern          日期时间格式 例如DATETIME_PATTERN
     * @return LocalDateTime 日期时间
     */
    public static LocalDateTime parseLocalDateTime(String localDateTimeStr, String pattern) {
        return LocalDateTime.parse(localDateTimeStr, DateTimeFormatter.ofPattern(pattern));
    }

    /**
     * 日期字符串转换为日期(java.time.LocalDate)
     *
     * @param localDateStr 日期字符串
     * @param pattern      日期格式 例如DATE_PATTERN
     * @return LocalDate 日期
     */
    public static LocalDate parseLocalDate(String localDateStr, String pattern) {
        return LocalDate.parse(localDateStr, DateTimeFormatter.ofPattern(pattern));
    }

    /**
     * 获取指定日期时间加上指定数量日期时间单位之后的日期时间.
     *
     * @param time       日期时间
     * @param num        数量
     * @param chronoUnit 日期时间单位
     * @return LocalDateTime 新的日期时间
     */
    @SuppressWarnings("unchecked")
    public static <T extends Temporal> T dateAdd(T time, int num, ChronoUnit chronoUnit) {
        return (T) time.plus(num, chronoUnit);
    }

    /**
     * 获取指定日期时间减去指定数量日期时间单位之后的日期时间.
     *
     * @param time       日期时间
     * @param num        数量
     * @param chronoUnit 日期时间单位
     * @return LocalDateTime 新的日期时间
     */
    @SuppressWarnings("unchecked")
    public static <T extends Temporal> T dateSub(T time, int num, ChronoUnit chronoUnit) {
        return (T) time.minus(num, chronoUnit);
    }

    /**
     * 根据ChronoUnit计算两个日期时间之间相隔日期时间
     *
     * @param start      开始日期时间
     * @param end        结束日期时间
     * @param chronoUnit 日期时间单位
     * @return long 相隔日期时间 正整数
     */
    public static long getChronoUnitBetween(Temporal start, Temporal end, ChronoUnit chronoUnit) {
        return Math.abs(start.until(end, chronoUnit));
    }

    /** 计算当前年龄的最小出生日期 */
    public static LocalDate calcFirstDayOfBirthYear(int age) {
        return LocalDate.now().minusYears(age + 1).plusDays(1);
    }

    /** 计算当前年龄的最大出生日期 */
    public static LocalDate calcLastDayOfBirthYear(int age) {
        return LocalDate.now().minusYears(age);
    }

    /**
     * 获取指定日期是星期几
     *
     * @param localDate 日期
     * @return 星期几
     */
    public static String getDayOfWeekStr(LocalDate localDate) {
        return localDate == null ? null : localDate.getDayOfWeek().getDisplayName(TextStyle.FULL, Locale.getDefault());
    }


    /**
     * 将传入的时间置为 零点
     * getFirstTimeOfDay
     */
    public static LocalDateTime atStartOfDay(LocalDateTime time) {
        return time.withHour((int) ChronoField.HOUR_OF_DAY.range().getMinimum())
                .withMinute((int) ChronoField.MINUTE_OF_HOUR.range().getMinimum())
                .withSecond((int) ChronoField.SECOND_OF_MINUTE.range().getMinimum())
                .withNano((int) ChronoField.NANO_OF_SECOND.range().getMinimum());
    }

    /**
     * 将传入的时间置为 23:59:59:999999999
     */
    public static LocalDateTime atEndOfDay(LocalDateTime time) {
        return time.withHour((int) ChronoField.HOUR_OF_DAY.range().getMaximum())
                .withMinute((int) ChronoField.MINUTE_OF_HOUR.range().getMaximum())
                .withSecond((int) ChronoField.SECOND_OF_MINUTE.range().getMaximum())
                .withNano((int) ChronoField.NANO_OF_SECOND.range().getMaximum());
    }


    /** 获取传入日期的当年第一天 */
    @SuppressWarnings("unchecked")
    public static <T extends Temporal> T getFirstDayOfYear(T time) {
        if (Objects.isNull(time)) {
            return null;
        }
        Temporal temporal = time.with(TemporalAdjusters.firstDayOfYear());
        return (temporal instanceof LocalDateTime) ? (T) atStartOfDay((LocalDateTime) temporal) : (T) temporal;
    }

    /** 获取传入日期的当年最后一天 */
    @SuppressWarnings("unchecked")
    public static <T extends Temporal> T getLastDayOfYear(T time) {
        if (Objects.isNull(time)) {
            return null;
        }
        Temporal temporal = time.with(TemporalAdjusters.lastDayOfYear());
        return (temporal instanceof LocalDateTime) ? (T) atEndOfDay((LocalDateTime) temporal) : (T) temporal;
    }

    /** 获取传入日期的当月第一天 */
    @SuppressWarnings("unchecked")
    public static <T extends Temporal> T getFirstDayOfMonth(T time) {
        if (Objects.isNull(time)) {
            return null;
        }
        Temporal temporal = time.with(TemporalAdjusters.firstDayOfMonth());
        return (temporal instanceof LocalDateTime) ? (T) atStartOfDay((LocalDateTime) temporal) : (T) temporal;
    }

    /** 获取传入日期的当月最后一天 */
    @SuppressWarnings("unchecked")
    public static <T extends Temporal> T getLastDayOfMonth(T time) {
        if (Objects.isNull(time)) {
            return null;
        }
        Temporal temporal = time.with(TemporalAdjusters.lastDayOfMonth());
        return (temporal instanceof LocalDateTime) ? (T) atEndOfDay((LocalDateTime) temporal) : (T) temporal;
    }

    /** 获取指定日期当周第一天的日期字符串,这里第一天为周一 */
    @SuppressWarnings("unchecked")
    public static <T extends Temporal> T getFirstDayOfWeek(T time) {
        if (Objects.isNull(time)) {
            return null;
        }
        Temporal temporal = time.with(ChronoField.DAY_OF_WEEK, ChronoField.DAY_OF_WEEK.range().getMinimum());
        return (temporal instanceof LocalDateTime) ? (T) atStartOfDay((LocalDateTime) temporal) : (T) temporal;
    }

    /** 获取指定日期当周最后一天的日期字符串,这里最后一天为周日 */
    @SuppressWarnings("unchecked")
    public static <T extends Temporal> T getLastDayOfWeek(T time) {
        if (Objects.isNull(time)) {
            return null;
        }
        Temporal temporal = time.with(ChronoField.DAY_OF_WEEK, ChronoField.DAY_OF_WEEK.range().getMaximum());
        return (temporal instanceof LocalDateTime) ? (T) atEndOfDay((LocalDateTime) temporal) : (T) temporal;
    }


    /**
     * 根据生日计算当前年龄并附带单位,不足一月显示月龄
     * 小于等于100天：x天
     * 100天到1岁：x个月
     * 1岁到3岁： x岁x月
     * 大于3岁：x岁
     *
     * @param birthday 生日
     * @return String
     * @author ljk
     * @since 2021/9/24 15:34
     */
    public static String toAgeStr(LocalDate birthday) {
        if (birthday == null) {
            return null;
        }
        LocalDate now = LocalDate.now();
        Period until = birthday.until(now);
        int years = until.getYears();
        int months = until.getMonths();
        int days = until.getDays();
        StringBuilder sb = new StringBuilder();
        if (years > 0) {
            sb.append(years).append("岁");
        }
        if (months > 0) {
            sb.append(months).append("月");
        }
        if (days > 0) {
            sb.append(days).append("天");
        }
        return sb.toString();
    }




    /* ---------------------------以为暂未验证-------------------------------------*/


    /**
     * 切割日期。按照周期切割成小段日期段。例如： <br>
     *
     * @param startDate 开始日期（yyyy-MM-dd）
     * @param endDate   结束日期（yyyy-MM-dd）
     * @param period    周期（天，周，月，年）
     * @return 切割之后的日期集合
     * <li>startDate="2019-02-28",endDate="2019-03-05",period="day"</li>
     * <li>结果为：[2019-02-28, 2019-03-01, 2019-03-02, 2019-03-03, 2019-03-04, 2019-03-05]</li><br>
     * <li>startDate="2019-02-28",endDate="2019-03-25",period="week"</li>
     * <li>结果为：[2019-02-28,2019-03-06, 2019-03-07,2019-03-13, 2019-03-14,2019-03-20,
     * 2019-03-21,2019-03-25]</li><br>
     * <li>startDate="2019-02-28",endDate="2019-05-25",period="month"</li>
     * <li>结果为：[2019-02-28,2019-02-28, 2019-03-01,2019-03-31, 2019-04-01,2019-04-30,
     * 2019-05-01,2019-05-25]</li><br>
     * <li>startDate="2019-02-28",endDate="2020-05-25",period="year"</li>
     * <li>结果为：[2019-02-28,2019-12-31, 2020-01-01,2020-05-25]</li><br>
     */
    public static List<String> listDateStrs(String startDate, String endDate, String period) {
        List<String> result = new ArrayList<>();
        DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(YYYY_MM_DD);
        LocalDate end = LocalDate.parse(endDate, dateTimeFormatter);
        LocalDate start = LocalDate.parse(startDate, dateTimeFormatter);
        LocalDate tmp = start;
        switch (period) {
            case DAY:
                while (start.isBefore(end) || start.isEqual(end)) {
                    result.add(start.toString());
                    start = start.plusDays(1);
                }
                break;
            case WEEK:
                while (tmp.isBefore(end) || tmp.isEqual(end)) {
                    if (tmp.plusDays(6).isAfter(end)) {
                        result.add(tmp + "," + end);
                    } else {
                        result.add(tmp + "," + tmp.plusDays(6));
                    }
                    tmp = tmp.plusDays(7);
                }
                break;
            case MONTH:
                while (tmp.isBefore(end) || tmp.isEqual(end)) {
                    LocalDate lastDayOfMonth = tmp.with(TemporalAdjusters.lastDayOfMonth());
                    if (lastDayOfMonth.isAfter(end)) {
                        result.add(tmp + "," + end);
                    } else {
                        result.add(tmp + "," + lastDayOfMonth);
                    }
                    tmp = lastDayOfMonth.plusDays(1);
                }
                break;
            case YEAR:
                while (tmp.isBefore(end) || tmp.isEqual(end)) {
                    LocalDate lastDayOfYear = tmp.with(TemporalAdjusters.lastDayOfYear());
                    if (lastDayOfYear.isAfter(end)) {
                        result.add(tmp + "," + end);
                    } else {
                        result.add(tmp + "," + lastDayOfYear);
                    }
                    tmp = lastDayOfYear.plusDays(1);
                }
                break;
            default:
                break;
        }
        return result;
    }

    public static void main(String[] args) {
        System.out.println("========切割日期。按照周期切割成小段日期段 ");
        List<String> dateStrs = listDateStrs("2019-01-30", "2020-12-13", YEAR);
        for (String dateStr : dateStrs) {
            System.out.println(dateStr);
        }

        System.out.println("========切割日期。按照周期切割成小段日期段 ");
        List<String> dateStrs1 = listDateStrs("2019-01-30", "2020-12-13", MONTH);
        for (String dateStr : dateStrs1) {
            System.out.println(dateStr);
        }

        System.out.println("========切割日期。按照周期切割成小段日期段 ");
        List<String> dateStrs2 = listDateStrs("2020-12-01", "2020-12-13", DAY);
        for (String dateStr : dateStrs2) {
            System.out.println(dateStr);
        }


        LocalDate calcFirstDayOfBirthYear = calcFirstDayOfBirthYear(10);
        System.out.println("calcFirstDayOfBirthYear = " + calcFirstDayOfBirthYear);
        LocalDate calcLastDayOfBirthYear = calcLastDayOfBirthYear(10);
        System.out.println("calcLastDayOfBirthYear = " + calcLastDayOfBirthYear);


    }

}



